/*
 * Copyright 2010 WANdisco, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. 
 */
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>

#include "com_googlecode_svnj_setuid_Setuid.h"

static jstring
make_jstring(JNIEnv *env, const char *c) {
  return c != NULL ? (*env)->NewStringUTF(env, c): NULL;
}

/* 0 ok, 1 error */
static int
copy_cstring(JNIEnv *env, jstring jstr, char *c, int len) {
  const char *tmp;
  jsize jlen = (*env)->GetStringLength(env, jstr);
  if (jlen >= len) {
    return 1;
  }
  tmp = (*env)->GetStringUTFChars(env, jstr, NULL);
  strncpy(c, tmp, jlen);
  c[jlen] = 0;
  (*env)->ReleaseStringUTFChars(env, jstr, tmp);
  return 0;
}

static void
throw_exception(JNIEnv *env, const char *cname, const char *msg) {
  jclass clazz = (*env)->FindClass(env, cname);
  if (clazz) {
    (*env)->ThrowNew(env, clazz, msg);
  }
}

#define BUF_SIZE 512

JNIEXPORT void JNICALL
Java_com_googlecode_svnj_setuid_Setuid_setID(JNIEnv *env, jclass clazz,
                                      jstring juser, jstring jgroup) {
  struct passwd pw;
  struct passwd *pwp;
  struct group gr;
  struct group *grp;
  int err;
  char target_uname[BUF_SIZE], target_gname[BUF_SIZE];
  char buf[BUF_SIZE];

  /* get uid from user name */
  if (copy_cstring(env, juser, target_uname, sizeof(target_uname))) {
    throw_exception(env, "java/lang/IllegalArgumentException",
                    "cannot copy username string");
    return;
  }
  if ((err = getpwnam_r(target_uname, &pw, buf, sizeof(buf), &pwp))
      || pwp == NULL) {
    if (err)
      sprintf(buf, "getpwnam() failed, error=%d", err);
    else
      sprintf(buf, "user '%s' not found", target_uname);
    throw_exception(env, "java/lang/RuntimeException", buf);
    return;
  }
  /* get gid from group name */
  if (copy_cstring(env, jgroup, target_gname, sizeof(target_gname))) {
    throw_exception(env, "java/lang/IllegalArgumentException",
                    "cannot copy groupname string");
    return;
  }
  if ((err = getgrnam_r(target_gname, &gr, buf, sizeof(buf), &grp))
    || grp == NULL) {
    if (err)
      sprintf(buf, "getgrnam() failed, error=%d", err);
    else
      sprintf(buf, "group '%s' not found", target_gname);
    throw_exception(env, "java/lang/RuntimeException", buf);
    return;
  }
  printf("got target uname/gname '%s'/'%s'\n", target_uname, target_gname);
  printf("setID() got uid=%d gid=%d\n", pw.pw_uid, gr.gr_gid);
}

JNIEXPORT jstring JNICALL
Java_com_googlecode_svnj_setuid_Setuid_getGroup(JNIEnv * env, jclass clazz) {
  struct group gr;
  struct group *grp;
  struct passwd pw;
  struct passwd *pwp;
  int err;
  char target_uname[BUF_SIZE], target_gname[BUF_SIZE];
  char buf[BUF_SIZE];
  jstring ret = NULL;
  gid_t gid;
  /* get user */
  uid_t uid = getuid();
  if (err = getpwuid_r(uid, &pw, buf, sizeof(buf), &pwp)) {
    sprintf(buf, "getpwuid() failed, error=%d", err);
    throw_exception(env, "java/lang/RuntimeException", buf);
    return ret;
  }

  /* get group from user */
  gid = pw.pw_gid;
  if (err = getgrgid_r(gid, &gr, buf, sizeof(buf), &grp)) {
    sprintf(buf, "getgrgid() failed, error=%d", err);
    throw_exception(env, "java/lang/RuntimeException", buf);
    return ret;
  }
  ret = make_jstring(env, gr.gr_name);
  return ret;
}

JNIEXPORT jstring JNICALL
Java_com_googlecode_svnj_setuid_Setuid_getUser(JNIEnv * env, jclass clazz) {
  struct passwd pw;
  struct passwd *pwp;
  jstring ret = NULL;
  char buf[BUF_SIZE];
  int err;
  uid_t uid = getuid();
  if (err = getpwuid_r(uid, &pw, buf, sizeof(buf), &pwp)) {
    sprintf(buf, "getpwuid() failed, error=%d", err);
    throw_exception(env, "java/lang/RuntimeException", buf);
    return ret;
  }
  ret = make_jstring(env, pw.pw_name);
  return ret;
}

JNIEXPORT void JNICALL
Java_com_googlecode_svnj_setuid_Setuid_callbackTest(JNIEnv *env, jclass clazz,
                                             jobject receiver) {
  jclass suid;
  jmethodID cback_instance, cback_static;

  suid = (*env)->FindClass(env, "com/wandisco/setuid/Setuid");
  cback_instance = (*env)->GetMethodID(env, clazz, "callMeBack", "()V");
  cback_static = (*env)->GetStaticMethodID(env, clazz,
                                           "callMeBack_static", "()V");
  (*env)->CallVoidMethod(env, receiver, cback_instance);
  (*env)->CallStaticVoidMethod(env, clazz, cback_static);
}


